﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HeresyManager
{
    public class CharacterCreator
    {
        private Character c;

        public Character Character { get { return c; } }

        public event EventHandler<CharacterFinishedArgs> CharacterFinished;


        public bool CharacterIsFinished { get; private set; }

        public IEnumerator<Comunication.Comunicator> CreateCharacter()
        {
            if (c != null)
                throw new Exception("Charackter Creation Alredy Started");
            c = new Character();
            c.Version = 1;



            var playername = new Comunication.EnterStringComunicator("Player Name");
            while (String.IsNullOrWhiteSpace(playername.Answer))
                yield return playername;
            c.PlayerName = playername.Answer;



            //Heimatwelt auswählen

            List<Tuple<String, Type>> homeworldList = new List<Tuple<string, Type>>();
            foreach (var assambly in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (var type in assambly.GetTypes())
                {
                    if (type.IsSubclassOf(typeof(HomeWorld)))
                    {
                        var cont = type.GetConstructor(new Type[0]);
                        var newHomeworld = cont.Invoke(new object[0]) as HomeWorld;
                        homeworldList.Add(new Tuple<String, Type>(newHomeworld.Name, type));
                    }
                }
            }

            var homeworld = new Comunication.ChooseOne<Type>("Select Homeworld", homeworldList.ToArray());
            while (homeworld.SelectedValue == null)
                yield return homeworld;
            var homeworldEnum = c.SetHomeWorld(homeworld.SelectedValue);

            while (homeworldEnum.MoveNext())
                yield return homeworldEnum.Current;

            //Eigenschaften auswürfeln

            var WS = new Comunication.RoleDice("Weapon Skill", c.HomeWorld.WeaponSkillDice);
            var BS = new Comunication.RoleDice("Balistic Skill", c.HomeWorld.BallisitcSkillDice);
            var S = new Comunication.RoleDice("Strength", c.HomeWorld.StrengthDice);
            var T = new Comunication.RoleDice("Toughness", c.HomeWorld.ToughnessDice);
            var Agi = new Comunication.RoleDice("Agility", c.HomeWorld.AgillityDice);
            var Int = new Comunication.RoleDice("Intelegence", c.HomeWorld.IntelligenceDice);
            var Wp = new Comunication.RoleDice("Willpower", c.HomeWorld.WillpowerDice);
            var Per = new Comunication.RoleDice("Perception", c.HomeWorld.PerceptionDice);
            var Fel = new Comunication.RoleDice("Fellowship", c.HomeWorld.FellowshipDice);

            var characteristics = new Comunication.MultyComunicator(WS, BS, S, T, Agi, Int, Wp, Per, Fel);
            while (characteristics.Comunicators.Any(t => (t as Comunication.RoleDice).Value == -1))
                yield return characteristics;

            c.WS = WS.Value;
            c.BS = BS.Value;
            c.S = S.Value;
            c.T = T.Value;
            c.Ag = Agi.Value;
            c.Int = Int.Value;
            c.Wp = Wp.Value;
            c.Per = Per.Value;
            c.Fel = Fel.Value;

            //Cariere Auswählen

            var vvv = (from a in c.HomeWorld.AccirableCareers select new Tuple<String, Type>(CreateInstance(a.Item1).Name, a.Item1));

            var careere = new Comunication.ChooseOne<Type>("Select Carere", vvv.ToArray());
            while (careere.SelectedValue == null)
                yield return careere;
            var careereEnum = c.SetCareer(careere.SelectedValue);
            while (careereEnum.MoveNext())
                yield return careereEnum.Current;


            //Stage four

            var wounds = new Comunication.RoleDice("Wounds", c.HomeWorld.WoundsDice);
            var fate = new Comunication.RoleDice("Fate", c.HomeWorld.FateDice);

            var stageFour = new Comunication.MultyComunicator(wounds, fate);
            while (stageFour.Comunicators.Any(t => (t as Comunication.RoleDice).Value == -1))
                yield return stageFour;

            c.TotalFatepoints = fate.Value;
            c.TotalWounds = wounds.Value;


            //Stage five

            if (c.Gender == Gender.Undefined)
            {
                var gender = new Comunication.ChooseOne<Gender>("Select Gender", Gender.Male, Gender.Female);
                while (gender.SelectedValue == Gender.Undefined)
                    yield return gender;
                c.Gender = gender.SelectedValue;
            }

            var charname = new Comunication.EnterStringComunicator("Character Name");
            while (String.IsNullOrWhiteSpace(charname.Answer))
                yield return charname;
            c.Name = charname.Answer;

            var build = new Comunication.ChooseOne<Build>("Build", (from b in c.HomeWorld.Builds where b.Gender == c.Gender select b).ToArray());
            var age = new Comunication.ChooseOne<Age>("Age", c.HomeWorld.Ages);

            var skin = new Comunication.ChooseOne<Color>("Skin Color", c.HomeWorld.SkinColors);
            var hair = new Comunication.ChooseOne<Color>("Hair Color", c.HomeWorld.HairColors);
            var eyes = new Comunication.ChooseOne<Color>("Eye Color", c.HomeWorld.EyeColors);

            var quirk = new Comunication.ChooseOne<Quirk>("Quirk", c.HomeWorld.Quirks);
            var origion = new Comunication.ChooseOne<Origin>(c.HomeWorld.OriginDescription, c.HomeWorld.Origins);

            var apperence = new Comunication.MultyComunicator(build, age, skin, hair, eyes, quirk, origion);
            while (apperence.Comunicators.Any(t => (t as Comunication.ChooseOne).SelectedIndex == -1))
                yield return apperence;
            c.Age += age.SelectedValue.Dice.Roll();
            c.Quirk = quirk.SelectedValue.ToString();
            c.Description = build.SelectedValue.Descripton + " Height: " + build.SelectedValue.Size.ToString() + " Weight: " + build.SelectedValue.Weight.ToString() + " Hair: " + hair.SelectedValue.Name + " Skin: " + skin.SelectedValue.Name + " Eye: " + eyes.SelectedValue.Name + " " + c.HomeWorld.OriginDescription + ": " + origion.SelectedValue.Description + " Age: " + c.Age;

            var divination = new Comunication.ChooseOne<Divination>("Divination", Divination.Divinations);
            while (divination.SelectedValue == null)
                yield return divination;
            c.Divination = divination.SelectedValue;

            c.AddExp(400);

            //TODO: StartPackeges aus Inqisitors Handbook

            var backgroundPacs = from bg in c.Career.BackgroundPackages where bg.IsPreconditionSattisfied(c) select new Tuple<String, BackgroundPackage>(bg.Name, bg);
            if (backgroundPacs.Count() > 0)
            {
                var bgArray = new Tuple<String, BackgroundPackage>[backgroundPacs.Count() + 1];
                int i = 1;
                foreach (var item in backgroundPacs)
                    bgArray[i++] = item;
                bgArray[0] = new Tuple<string, BackgroundPackage>("No Backgrundpackage", null);
                var bgCom = new Comunication.ChooseOne<BackgroundPackage>("Backgroundpackage", bgArray);
                yield return bgCom;
                if (bgCom.SelectedValue != null)
                {
                    var begSelect = bgCom.SelectedValue.Accire(c);
                    while (begSelect.MoveNext())
                        yield return begSelect.Current;
                }
            }

            //This is the end
            CharacterIsFinished = true;
            Character.IsFinished = true;
            if (CharacterFinished != null)
                CharacterFinished(this, new CharacterFinishedArgs(c));
        }

        private static Career CreateInstance(Type type)
        {
            var con = type.GetConstructor(new Type[0]);
            var obj = con.Invoke(new Object[0]);
            var car = obj as Career;
            return car;
        }

        public class CharacterFinishedArgs : EventArgs
        {
            private Character character;

            public CharacterFinishedArgs(Character character)
            {
                this.character = character;
            }

            public Character Character
            {
                get { return character; }
            }
        }

    }
}
